#ifndef _ENCRYPT_AES
#define _ENCRYPT_AES

/*
 The following lines have been excerpted from http://www.entropy.ch/blog/Developer/2009/01/11/OpenSSL-Public-Key-and-PKI-Crash-Course-Part-1-4.html
 
 Let's work through an example. First we decide on (make up) a random key for the symmetric encryption and stick that key into a file. 
 We'll use the incredibly hard to guess 'foobar':

	echo -n foobar > test.txt
	
 Then we encrypt the large word list file with the AES cipher using this password and store the encrypted data in another file:

	openssl enc -aes-128-cbc -salt -pass file:test.txt < /usr/share/dict/words > words.dat

 (Technically, 'foobar' is not the key but a password from which openssl derives the actual key used by the algorithm.)
 Now we asymmetrically encrypt the small password file with the recipient's public key. 
 You already know how to do this, because it is exactly what we used above:

	openssl rsautl -encrypt -pubin -inkey public-key.pem < test.txt > test-encrypted.txt

 It's exactly the same except that instead of 'Alice in Wonderland' we encrypt 'foobar'.

 We now have the two parts that together make up the protected message, they are in the two files words.dat and test-encrypted.txt. 
 These two have to be sent to the recipient, and if someone intercepts them in transit, they are useless.
 For the recipient of the message, the first step is to decrypt the symmetric encryption key. 
 Again, you already know how this works because it is identical to the rsautl example above:

	openssl rsautl -decrypt -inkey private-key.pem < test-encrypted.txt > test.txt

 test.txt now contains 'foobar' and we can use this to decrypt the word list:

	openssl enc -aes-128-cbc -d -pass file:test.txt < words.dat > words.txt

 You might be wondering why we don't simply use the symmetric algorithm directly to exchange data, 
 instead of going to such lengths to combine the two methods. The reason is that asymmetric keys have desirable properties 
 regarding key distribution and ownership, especially when encryption is used on a large scale, such as between millions of users 
 on the Internet. The Wikipedia article linked above has more information.

*/

#include <openssl/evp.h>
#include <string>
#include <vector>

class encrypt_aes
{
public:

  	static const unsigned short AES_BLOCK_SIZE = 256;
  	static const unsigned short KEY_BLOCK_SIZE = 32;

	encrypt_aes(const char* _input, const char* _passwd);
	encrypt_aes();

 	/*
 	* Encrypt *len bytes of data
 	* All data going in & out is considered binary (unsigned char[])
 	*/
	static unsigned char *aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len);

	/*
 	* Decrypt *len bytes of ciphertext
 	*/
	static unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len);
	
	static void encrypt_into(const char* _input, const char* _passwd, const std::string& _cipher_file, const std::string& _key_file, const std::string& _iv_file);
	static const std::string decrypt_from(const std::string& _cipher_file, const std::string& _key_file, 
			const std::string& _iv_file, const std::vector<unsigned short>& _substring_indices);
	//static const std::string decrypt_from(const std::string& _cipher_file, const std::string& _key_file, const std::string& _iv_file, const char* _original);

	//int encrypt(const char* _input, int *_inputlen);
	int encrypt();
	const std::string decrypt();

	void save(const std::string& _cipher_filename, const std::string& _key_filename, const std::string& _iv_filename);
	bool read(const std::string& _ciphertext_file, const std::string& _key_file, const std::string& _iv_file);
	
	bool check_decrypt(const char* _input);

	~encrypt_aes();

	static int unpack(const std::string& _filename, unsigned char** _value);

private:

	// encrypt the AES cipher using the password pass
	int aes_decrypt_init();
	int aes_encrypt_init();
	int aes_encrypt_pass(unsigned char *key_data, int key_data_len, unsigned char *salt, int nround = 15);

	unsigned char *ciphertext;
	int ciphertextlen;	

	char *plaintext;
	const char* passwd;
	const char* input;

	/**
 	* Create an 256 bit key and IV using the supplied key_data. salt can be added for taste.
 	* Fills in the encryption and decryption ctx objects and returns 0 on success
 	**/
	unsigned char key[KEY_BLOCK_SIZE], iv[KEY_BLOCK_SIZE];
 	unsigned char *key_data;		 
	int key_data_len;
	
	void pack(const std::string& _filename, unsigned char* _str, int _len);
 	
	/* The enc/dec functions deal with binary data and not C strings. strlen() will 
	return length of the string without counting the '\0' string marker. We always
       	pass in the marker byte to the encrypt/decrypt functions so that after decryption 
       	we end up with a legal C string */    
	
	/* "opaque" encryption, decryption ctx structures that libcrypto uses to record
     	status of enc/dec operations */
  	
	EVP_CIPHER_CTX en, de;


};

#endif
